//***************************************************
//	Opposing Forces Weapon Barnacle.
//
//			Made by Demiurge.
//		Based on code Grapping hook,
//	by Phantasiere (nrich@iinet.net.au)
//
//		Separate thanks for BUzer because 
//  without his councils, the code would be a 
//		 nightmare of the programmer.
//
//FGD:
//weapon_grapple
//
//NOTE:
//		1) grapple monsters
//		2) grapple textures (xeno_...)
//
//***************************************************

#include "extdll.h" 
#include "util.h" 
#include "cbase.h" 
#include "monsters.h" 
#include "weapons.h" 
#include "nodes.h" 
#include "player.h" 
#include "soundent.h" 
#include "shake.h" 
#include "gamerules.h"
#include "vector.h"


//****************************
//
//		Barnacle Tongue
//
//****************************

class CGrappleHook : public CBaseEntity
{ 
public: 

	void Spawn( void ); 
	void Precache( void ); 
	void EXPORT Move( void ); 
	void EXPORT Hit( CBaseEntity* ); 
	void Killed(entvars_t *pev, int gib); //Removes grapple
	static CGrappleHook* Create( Vector, Vector, CBasePlayer*);
	int m_Chain; 
	int m_iIsMoving; 
	int m_iTrail_Length;
	CBasePlayer *myowner;

	unsigned short m_usTongue;
};

LINK_ENTITY_TO_CLASS( proj_hook, CGrappleHook );


void CGrappleHook :: Spawn( void ) 
{ 
	Precache( ); 

	SET_MODEL( ENT(pev), "models/v_bgrap_tonguetip.mdl" );
	pev->movetype = MOVETYPE_FLY; 
	pev->solid = SOLID_BBOX; 
	pev->rendermode = kRenderNormal;
	pev->renderamt = 0;
	pev->effects = EF_NODRAW;

	UTIL_SetSize( pev, Vector(0,0,0), Vector(0,0,0) ); 
	UTIL_SetOrigin( pev, pev->origin ); 

	pev->classname = MAKE_STRING( "proj_hook" );

	SetThink( Move ); 
	SetTouch( Hit );

	UTIL_MakeVectors(pev->angles);
	pev->velocity = gpGlobals->v_forward * 700; 
	pev->gravity = 0; 
	pev->nextthink = gpGlobals->time + 0.13; 
	pev->dmg = 0;
}

void CGrappleHook :: Precache( void ) 
{ 
	m_Chain = PRECACHE_MODEL( "sprites/tongue.spr" ); 
	PRECACHE_MODEL( "models/v_bgrap_tonguetip.mdl" ); 

	PRECACHE_SOUND("weapons/bgrapple_impact.wav");	//hit tongue
}

void CGrappleHook :: Hit( CBaseEntity* Target ) 
{ 
	TraceResult TResult; 
	Vector StartPosition;

	EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/bgrapple_impact.wav", 1, ATTN_NORM);

	Vector delta = Vector( 8, 8, 0 );
	Vector mins = pev->origin - delta;
	Vector maxs = pev->origin + delta;
	maxs.z = pev->origin.z;

#ifndef CLIENT_DLL

	CBaseEntity *pList[1];
	int count = UTIL_EntitiesInBox( pList, 1, mins, maxs, (FL_CLIENT|FL_MONSTER) );
	if ( count )
	{
		pev->velocity = pev->velocity.Normalize( );
		myowner->m_afPhysicsFlags |= PFLAG_ON_GRAPPLE; //Set physics flag to "on grapple"
		myowner->pev->movetype = MOVETYPE_BOUNCE; //Remove gravity effect on player
	}
	else
	{

	TraceResult tr;
	float rgfl1[3];
	float rgfl2[3];
	const char *pTextureName;
	Vector vecSrc = pev->origin;
	Vector vecEnd = vecSrc + gpGlobals->v_forward * 8;

	UTIL_MakeVectors (pev->v_angle);
	UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, ENT( pev ), &tr );

	CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
		vecSrc.CopyToArray(rgfl1);
		vecEnd.CopyToArray(rgfl2);
		if (pEntity)
			pTextureName = TRACE_TEXTURE( ENT(pEntity->pev), rgfl1, rgfl2 );
		else
			pTextureName = TRACE_TEXTURE( ENT(0), rgfl1, rgfl2 );

		if (memcmp (pTextureName, "xeno_", 5) == 0)
		{
			pev->velocity = pev->velocity.Normalize( );
			myowner->m_afPhysicsFlags |= PFLAG_ON_GRAPPLE; //Set physics flag to "on grapple"
			myowner->pev->movetype = MOVETYPE_BOUNCE; //Remove gravity effect on player
		}
			else
			Killed(pev, 0);
	}
#endif

}

void CGrappleHook :: Killed(entvars_t *pev, int gib)
{ 
		myowner->pev->movetype = MOVETYPE_WALK; //Re-apply gravity
		myowner->m_afPhysicsFlags &= ~PFLAG_ON_GRAPPLE; //Remove "on grapple" flag
		myowner->m_iGrappleExists = 0;
		SetThink (NULL);
		SetTouch (NULL);

		SUB_Remove( ); 
}

CGrappleHook* CGrappleHook :: Create( Vector Pos, Vector Aim, CBasePlayer* Owner ) 
{ 
	CGrappleHook* Hook = GetClassPtr( (CGrappleHook*)NULL ); 
	UTIL_SetOrigin( Hook->pev, Pos ); 
	Hook->pev->angles = Aim; 
	Hook->Spawn( ); 
	Hook->SetTouch( CGrappleHook :: Hit ); 
	Hook->pev->owner = Owner->edict( );
	Hook->myowner = Owner;
	return Hook; 
}

void CGrappleHook :: Move( void ) 
{ 
	if( !myowner->IsAlive( ) ) //if owner is dead is
	{
		Killed(pev, 0); //Remove grapple
		return;
	}


	if(!(myowner->pev->button & (IN_ATTACK|IN_ATTACK2) )) //If owner is not pushing attack buttons
	{
	Killed(pev, 0); //Remove grapple
	return;
	}

/*	if( myowner->m_afPhysicsFlags & PFLAG_ON_GRAPPLE) //If we are on a grapple
	{
	myowner->pev->velocity * 10;
	}
*/
	

		MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); //Draw 'chain'
		WRITE_BYTE( TE_BEAMENTPOINT ); 
		WRITE_SHORT( myowner->entindex() + 0x1000 );
		WRITE_COORD( pev->origin.x );
		WRITE_COORD( pev->origin.y );
		WRITE_COORD( pev->origin.z );
		WRITE_SHORT( m_Chain ); 
		WRITE_BYTE( 1 );	//start frame
		WRITE_BYTE( 0 );	//rate
		WRITE_BYTE( 1 );	//life
		WRITE_BYTE( 15 );	//width 
		WRITE_BYTE( 0);		//noise
		WRITE_BYTE( 255 );	//r
		WRITE_BYTE( 255 );	//g
		WRITE_BYTE( 255 );	//b
		WRITE_BYTE( 255 );	//brightnes
		WRITE_BYTE( 0);		//scrollspeed
		MESSAGE_END( );

	pev->nextthink = gpGlobals->time + 0.1;
}



//****************************
//
//		Barnacle Weapon
//
//****************************
enum grapple_e {
	GRAPPLE_BREATHE = 0,
	GRAPPLE_LONGIDLE,
	GRAPPLE_SHORTIDLE,
	GRAPPLE_COUGH,
	GRAPPLE_DOWN,
	GRAPPLE_UP,
	GRAPPLE_FIRE,
	GRAPPLE_FIREWAITING,
	GRAPPLE_FIREREACHED,
	GRAPPLE_FIRETRAVEL,
	GRAPPLE_FIRERELEASE
};

LINK_ENTITY_TO_CLASS( weapon_grapple, CGrapple );

BOOL CGrapple::IsUseable( void )
{
	return TRUE;
}

void CGrapple :: Spawn( void ) 
{ 
	pev->classname = MAKE_STRING( "weapon_grapple" ); 

	Precache( ); 

	SET_MODEL(ENT(pev), "models/w_bgrap.mdl");
	m_iId = WEAPON_GRAPPLE; 
	m_iDefaultAmmo = 1; 
		FallInit( ); 
}

void CGrapple :: Precache( void ) 
{ 
	PRECACHE_MODEL("models/p_bgrap.mdl");
	PRECACHE_MODEL("models/v_bgrap.mdl");
	PRECACHE_MODEL("models/w_bgrap.mdl");
	PRECACHE_MODEL( "sprites/tongue.spr" );
	PRECACHE_MODEL( "models/v_bgrap_tonguetip.mdl" ); 

	PRECACHE_SOUND("weapons/alienweap_draw.wav");	//pick up
	PRECACHE_SOUND("weapons/bgrapple_pull.wav");	//tongue pull player
	PRECACHE_SOUND("weapons/bgrapple_fire.wav");	//fire
	PRECACHE_SOUND("weapons/bgrapple_impact.wav");	//hit tongue
	PRECACHE_SOUND("weapons/bgrapple_release.wav");	//remove tongue
	PRECACHE_SOUND("weapons/bgrapple_wait.wav");	//idle
	PRECACHE_SOUND("weapons/bgrapple_cough.wav");	//idle

	UTIL_PrecacheOther("proj_hook");

	m_usTongue = PRECACHE_EVENT ( 1, "events/grapple.sc" );
	m_usTongue2 = PRECACHE_EVENT ( 1, "events/grapple.sc" );

}

int CGrapple::AddToPlayer( CBasePlayer *pPlayer )
{
	if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
	{
		MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
			WRITE_BYTE( m_iId );
		MESSAGE_END();
		return TRUE;
	}
	return FALSE;
}


int CGrapple :: GetItemInfo( ItemInfo* Info ) 
{ 
	Info->pszName = STRING( pev->classname ); 
	Info->pszAmmo1 = NULL; 
	Info->iMaxAmmo1 = -1; 
	Info->pszAmmo2 = NULL; 
	Info->iMaxAmmo2 = -1; 
	Info->iMaxClip = 1; 
	Info->iSlot = 0; 
	Info->iPosition = 2; 
	Info->iFlags = 0;
	Info->iId = WEAPON_GRAPPLE; 
	Info->iWeight = GRAPPLE_WEIGHT;
	return 1; 
}

BOOL CGrapple :: Deploy( void ) 
{ 
	return DefaultDeploy( "models/v_bgrap.mdl", "models/p_bgrap.mdl", GRAPPLE_UP, "hive", 0 );
	m_flTimeWeaponIdle = gpGlobals->time;
	SetThink (WeaponIdle);
}

void CGrapple::Holster( int skiplocal )
{
	STOP_SOUND( ENT(pev), CHAN_WEAPON, "weapons/bgrapple_wait.wav" );
	m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
}

void CGrapple :: PrimaryAttack( ) 
{ 
	if( m_pPlayer->m_iGrappleExists ) //if player already has a grapple
	{
		return;
	}

	if(m_flNextPrimaryAttack > gpGlobals->time)
	{
		return;
	}

	int flags;
#if defined( CLIENT_WEAPONS )
	flags = FEV_NOTHOST;
#else
	flags = 0;
#endif


	SendWeaponAnim( GRAPPLE_FIREWAITING, 1 );
	EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/bgrapple_fire.wav", 1, ATTN_NORM);
	m_pPlayer->m_iGrappleExists = 1;
	m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); 

	UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); 
	Vector AimingDir = gpGlobals->v_forward; 
	Vector GunPosition = m_pPlayer->GetGunPosition( ); 

	GunPosition = GunPosition + gpGlobals->v_up * -4 + gpGlobals->v_right * 3 + gpGlobals->v_forward * 16;
	m_pPlayer->m_MyGrapple = CGrappleHook :: Create( GunPosition, m_pPlayer->pev->v_angle, m_pPlayer );


	EMIT_SOUND( ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/bgrapple_pull.wav", 1, ATTN_NORM);
//		ALERT( at_console, "Think launched!!!\n" );

	SetThink(FlyThink);
	pev->nextthink = gpGlobals->time + 0.1;

}

void CGrapple :: FlyThink( void ) 
{
	
	if( m_pPlayer->m_afPhysicsFlags & PFLAG_ON_GRAPPLE ) //If we are on a grapple
	{
//		ALERT( at_console, "Splat, flying!!!\n" );

	SendWeaponAnim( GRAPPLE_FIRETRAVEL, 1 );
		
	SetThink(PullThink);
	}

	if ( !m_pPlayer->m_iGrappleExists )
	{
	SendWeaponAnim( GRAPPLE_FIRERELEASE, 1 );
	EMIT_SOUND( ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/bgrapple_release.wav", 1, ATTN_NORM);

	m_flNextPrimaryAttack = m_flNextPrimaryAttack + 1;

	if (m_flNextPrimaryAttack < UTIL_WeaponTimeBase() )
	{
		m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.25;
	}

	SetThink (WeaponIdle);
	pev->nextthink = gpGlobals->time + 1;
	m_flTimeWeaponIdle = gpGlobals->time + 0.5;
	}

	pev->nextthink = gpGlobals->time + 0.1;
}

void CGrapple :: PullThink( void ) 
{
	if( m_pPlayer->m_afPhysicsFlags & PFLAG_ON_GRAPPLE ) //If we are on a grapple
	{
	TraceResult tr;

	UTIL_MakeVectors (m_pPlayer->pev->v_angle);
	Vector vecSrc = m_pPlayer->GetGunPosition();
	Vector vecEnd = vecSrc + gpGlobals->v_forward * 48;

#ifndef CLIENT_DLL
	UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr );


	ClearMultiDamage();
	CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
	pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgGrapple, gpGlobals->v_forward, &tr, DMG_CLUB || DMG_ALWAYSGIB ); 
	ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev );
#endif
		pev->nextthink = gpGlobals->time + 0.1;
	}
	else 
	{

	SendWeaponAnim( GRAPPLE_FIRERELEASE, 1 );
	EMIT_SOUND( ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/bgrapple_release.wav", 1, ATTN_NORM);

	m_flNextPrimaryAttack = m_flNextPrimaryAttack + 1;

	if (m_flNextPrimaryAttack < UTIL_WeaponTimeBase() )
	{
		m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.25;
	}

	SetThink (WeaponIdle);
	pev->nextthink = gpGlobals->time + 1;
	m_flTimeWeaponIdle = gpGlobals->time + 0.5;
	}
}

void CGrapple :: WeaponIdle( void ) 
{
	int iAnim;
	int fRand;
	m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
		STOP_SOUND( ENT(pev), CHAN_WEAPON, "weapons/bgrapple_wait.wav" );

		pev->nextthink = gpGlobals->time + 0.5;

	if ( m_flTimeWeaponIdle > gpGlobals->time )
	return;

	fRand = RANDOM_LONG (0, 3);

	if ( fRand < 3 )
	{
	switch ( RANDOM_LONG (0, 1) )
		{
	case 0:
		iAnim = GRAPPLE_BREATHE ;
		m_flTimeWeaponIdle = gpGlobals->time + 3;
		SendWeaponAnim( iAnim, 1 );
	break;

	case 1:	
		iAnim = GRAPPLE_LONGIDLE;
		m_flTimeWeaponIdle = gpGlobals->time + 10;
		SendWeaponAnim( iAnim, 1 );
	break;
		}
	}
	else
	{
	switch ( RANDOM_LONG (0, 1) )
		{
	case 0:	
		iAnim = GRAPPLE_SHORTIDLE;
		m_flTimeWeaponIdle = gpGlobals->time + 0.5;
		EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/bgrapple_wait.wav", 1, ATTN_NORM);
		SendWeaponAnim( iAnim, 1 );
	break;

	case 1:	
		iAnim = GRAPPLE_COUGH;
		m_flTimeWeaponIdle = gpGlobals->time + 4.5;
		EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/bgrapple_cough.wav", 1, ATTN_NORM);
		SendWeaponAnim( iAnim, 1 );
	break;
		}
	}
}


/* 
Now open Player.h and add these lines at about line 35:

//Begin Grapple

#define PFLAG_ON_GRAPPLE ( 1<<6 ) //Added Physics flag for grapple

//End Grapple

Also add these lines into the player class at about line 173: 

//Begin grapple 

int m_iGrappleExists; 
CBaseEntity *m_MyGrapple; //Grappling hook

//End Grapple

Open Player.cpp and add these lines to the PlayerPreThink function at the line 2222:

//Begin Grapple

if( m_afPhysicsFlags & PFLAG_ON_GRAPPLE) //If we are on a grapple

pev->velocity = (m_MyGrapple->pev->origin - pev->origin) * 2;

//End Grapple
*/